package com.jetpackframework.virtual;

import android.app.Application;
import android.app.Instrumentation;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;

import com.blankj.utilcode.util.FileUtils;
import com.jetpackframework.ContextUtil;
import com.jetpackframework.Reflector;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;

import dalvik.system.DexClassLoader;

/**
 * APK文件解析器   一个APK文件对应一个VirtualApk
 */
public class VirtualApk {
    private PackageInfo packageInfo;
    private ClassLoader classLoader;
    private Resources resources;
    private String mNativeLibPath;
    private Instrumentation instrumentation;
    private Resources.Theme mPluginTheme;
    private Application application;
    Map<String, List<IntentFilter>> intentFilters;


    public VirtualApk(File apkFile) {
        try {
            instrumentation = getInstrumentation();
        } catch (Reflector.ReflectedException e) {
            e.printStackTrace();
        }

        try {
            intentFilters = PackageParser.parserApk(apkFile.getAbsolutePath());
        } catch (Reflector.ReflectedException e) {
            e.printStackTrace();
        }
        packageInfo = ContextUtil.get().getPackageManager().getPackageArchiveInfo(apkFile.getAbsolutePath(),
                PackageManager.GET_ACTIVITIES|PackageManager.GET_PROVIDERS|PackageManager.GET_SERVICES|PackageManager.GET_RECEIVERS
                        |PackageManager.GET_PERMISSIONS|PackageManager.GET_META_DATA|PackageManager.GET_INSTRUMENTATION|PackageManager.GET_SIGNATURES|PackageManager.GET_INTENT_FILTERS);
        mNativeLibPath =ContextUtil.get().getFilesDir()+"/virtuallib";
        String outputOdex = ContextUtil.get().getFilesDir()+"/virtualcache";
        FileUtils.createOrExistsDir(mNativeLibPath);
        FileUtils.createOrExistsDir(outputOdex);
        classLoader = new DexClassLoader(apkFile.getAbsolutePath(), outputOdex, mNativeLibPath, ContextUtil.get().getClassLoader());
        Resources hostResources = ContextUtil.get().getResources();
        AssetManager assetManager = null;
        try {
            assetManager = createAssetManager(apkFile);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        resources = new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
        mPluginTheme = resources.newTheme();
        mPluginTheme.setTo(ContextUtil.get().getTheme());
        Bundle metaData = packageInfo.applicationInfo.metaData;
        //TODO 执行Application
        makeApplication(packageInfo.applicationInfo);
        //注册receiver
        if (packageInfo.receivers != null && packageInfo.receivers.length > 0){
            for (ActivityInfo info : packageInfo.receivers) {
                BroadcastReceiver receiver = null;
                try {
                    receiver = BroadcastReceiver.class.cast(classLoader.loadClass(info.name).newInstance());
                    List<IntentFilter> intentFilters = this.intentFilters.get(info.packageName + "/" + info.name);
                    for (IntentFilter intentFilter : intentFilters) {
                        ContextUtil.get().registerReceiver(receiver, intentFilter);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private AssetManager createAssetManager(File apkFile) throws InstantiationException, IllegalAccessException {
        AssetManager am = AssetManager.class.newInstance();
        try {
            Reflector.with(am).method("addAssetPath", String.class).call(apkFile.getAbsolutePath());
        } catch (Reflector.ReflectedException e) {
            e.printStackTrace();
        }
        return am;
    }

    private void makeApplication(ApplicationInfo applicationInfo) {
        String className = applicationInfo.className;
        if (className == null){
            className = "android.app.Application";
        }
        try {

            application = instrumentation.newApplication(getClassLoader(),className,ContextUtil.get());
            instrumentation.callApplicationOnCreate(application);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }
    public ActivityInfo getActivityInfo(ComponentName component) {
        for (ActivityInfo info : packageInfo.activities) {
            Log.e("TAG----","info.packageName="+info.packageName+",info.name="+info.name);

            if (info.packageName.equals(component.getPackageName()) && info.name.equals(component.getClassName())){
                return info;
            }
        }
        return null;
    }

    private Instrumentation getInstrumentation() throws Reflector.ReflectedException {
        Object activityThread = Reflector.on("android.app.ActivityThread").method("currentActivityThread").call();
        Instrumentation instrumentation = Reflector.with(activityThread).method("getInstrumentation").call();
        return instrumentation;
    }
    public String getPackageName() {
        return packageInfo.packageName;
    }

    public ClassLoader getClassLoader() {
        return classLoader;
    }

    public Resources getResources() {
        return resources;
    }

    public PackageInfo getPackageInfo() {
        return packageInfo;
    }
    public Application getApplication(){
        return application;
    }

    public Intent getLaunchIntent() {
        for (ActivityInfo activityInfo : packageInfo.activities){
            List<IntentFilter> filters = intentFilters.get(activityInfo.packageName+"/"+activityInfo.name);
            for (IntentFilter filter : filters){
                if (filter.hasAction(Intent.ACTION_MAIN) && filter.hasCategory(Intent.CATEGORY_LAUNCHER)){
                    Intent intent = new Intent();
                    ComponentName componentName = new ComponentName(activityInfo.packageName,activityInfo.name);
                    intent.setComponent(componentName);
                    return intent;
                }
            }
        }
        return null;
    }

    public ActivityInfo getReceiverInfo(ComponentName component) {
        for (ActivityInfo info : packageInfo.receivers){
            if (info.packageName.equals(component.getPackageName()) && info.name.equals(component.getClassName())){
                return info;
            }
        }
        return null;
    }

    public ServiceInfo getServiceInfo(ComponentName component) {
        for (ServiceInfo info : packageInfo.services){
            if (info.packageName.equals(component.getPackageName()) && info.name.equals(component.getClassName())){
                return info;
            }
        }
        return null;
    }

    public ProviderInfo getProviderInfo(Uri contentUri) {
        String authority = contentUri.getAuthority();
        return getProviderInfo(authority);
    }

    public ProviderInfo getProviderInfo(String authority){
        for (ProviderInfo info : packageInfo.providers){
            if (info.authority.equals(authority)){
                return info;
            }
        }
        return null;
    }
    public ResolveInfo resolveActivity(Intent intent, int flags) {
        ResolveInfo info = null;
        ComponentName componentName = intent.getComponent();
        for (ActivityInfo activityInfo : packageInfo.activities){
            if (activityInfo.packageName.equals(componentName.getPackageName()) && activityInfo.name.equals(componentName.getClassName())){
                info = new ResolveInfo();
                info.activityInfo = activityInfo;
                break;
            }
        }
        return info;
    }

    public Resources.Theme getTheme(){
        return mPluginTheme;
    }
    public ResolveInfo resolveService(Intent intent, int flags) {
        return null;
    }

    public ProviderInfo getProviderInfo(ComponentName component) {
        for (ProviderInfo providerInfo : packageInfo.providers){
            if (providerInfo.packageName.equals(component.getPackageName()) && providerInfo.name.equals(component.getClassName())){
                return providerInfo;
            }
        }
        return null;
    }

    public InstrumentationInfo getInstrumentationInfo(ComponentName componentName){
        for (InstrumentationInfo instrumentationInfo : packageInfo.instrumentation){
            if (instrumentationInfo.packageName.equals(componentName.getPackageName()) && instrumentationInfo.name.equals(componentName.getClassName())){
                return instrumentationInfo;
            }
        }
        return null;
    }
    public int getThemeByComponent(ComponentName component) {
        int theme = getActivityInfo(component).theme;
        if (theme == 0){
            theme = packageInfo.applicationInfo.theme;
        }
        if (theme == 0){
            theme = selectDefaultTheme(Build.VERSION.SDK_INT);
        }
        return theme;
    }

    private int selectDefaultTheme(int sdkInt) {
        if (sdkInt < Build.VERSION_CODES.HONEYCOMB){
            return android.R.style.Theme;
        }else if (sdkInt < Build.VERSION_CODES.ICE_CREAM_SANDWICH){
            return android.R.style.Theme_Holo;
        }else if (sdkInt < Build.VERSION_CODES.N){
            return android.R.style.Theme_DeviceDefault;
        }else {
            return android.R.style.Theme_DeviceDefault_Light_DarkActionBar;
        }
    }
}
